"""
-------------------------------
- File_Name: security.py
- Date: 2025/2/6
- Author: yangzhide
- Email: xxzjyzd@126.com
- Description: 安全相关功能，密码哈希、JWT令牌和验证等
-------------------------------
"""
from fastapi import Depends
from jwt import ExpiredSignatureError
from passlib.context import CryptContext
from datetime import datetime, timedelta, timezone

from config import get_api_environment_var
import jwt
from app.models.system.admin import User, Role, UserRole
from app.dependencies.authDepend import oauth2_scheme
from app.core.exceptions import AuthenticationError
from app.logging.config import log
from app.core.ctx import CTX_USER, CtxUser

# 获取环境变量
api_environment = get_api_environment_var()
# 密码加密
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


def get_password_hash(password) -> str:
    """密码哈希"""
    return pwd_context.hash(password)


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """验证密码"""
    return pwd_context.verify(plain_password, hashed_password)


async def create_access_token(data: dict, expires_delta: timedelta = None):
    """生成JWT token"""
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=3)  # 测试3分钟

    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, api_environment.secret_key, algorithm=api_environment.algorithm)
    return encoded_jwt


async def user_to_create_token(user: User):
    token_expires = timedelta(minutes=api_environment.access_token_expire_minutes)
    refresh_token_expires = timedelta(minutes=api_environment.refresh_token_expire_minutes)

    access_token = await create_access_token(
        data={"user_name": user.user_name, "user_code": user.user_code}, expires_delta=token_expires
    )
    refresh_token = await create_access_token(
        data={"user_name": user.user_name, "user_code": user.user_code}, expires_delta=refresh_token_expires
    )
    # 更新登录时间
    user.last_login = datetime.now()
    # 更新为在线状态
    user.online = "1"
    await user.save()

    return access_token, refresh_token


async def get_current_user(token: str = Depends(oauth2_scheme)):
    """验证token,并存储用户名/编号到请求上下文
    code为9999：前端重定向到登录页面
    """
    try:
        payload = jwt.decode(token, api_environment.secret_key, algorithms=[api_environment.algorithm])
        user_name = payload.get("user_name")
        user_code = payload.get("user_code")
        user_obj = await User.get_or_none(user_name=user_name, user_code=user_code)
        user_role_id = await UserRole.filter(user_id=user_obj.id).values_list("role_id", flat=True)

        if not user_obj:
            log.info("无效的验证凭证")
            raise AuthenticationError(code=9999, msg="无效的验证凭证")
        user_role_code = await Role.filter(id__in=user_role_id).values_list("role_code", flat=True)
        # 存储到请求上下文,用户名称及角色编号
        user = CtxUser(
            user_name=user_obj.user_name,
            user_code=user_obj.user_code,
            user_role_id=user_role_id,
            user_role_code=user_role_code,
        )
        CTX_USER.set(user)
        return user_obj
    except ExpiredSignatureError:
        log.info("凭证已过期,请重新登录")
        raise AuthenticationError(code=9999, msg="凭证已过期,请重新登录")
    except jwt.InvalidTokenError:
        log.info("无效的验证凭证")
        raise AuthenticationError(code=9999, msg="无效的验证凭证")
    except Exception:
        log.info("无效的验证凭证")
        raise AuthenticationError(code=9999, msg="无效的验证凭证")
